package com.syf.service.impl;

import com.alibaba.fastjson.JSON;
import com.syf.feign1.JwtToken2;
import com.syf.feign1.OAuth2FeignClient2;
import com.syf.geetest.GeetestLib;
import com.syf.geetest.GeetestLibResult;
import com.syf.model.LoginForm;
import com.syf.model.LoginUser;
import com.syf.service.LoginService;
import com.syf.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class LoginServiceImpl implements LoginService {

    @Autowired
    private OAuth2FeignClient2 oAuth2FeignClient2; //OAuth2 -> 请求获取token

    @Value("${basic.token:Basic Y29pbi1hcGk6Y29pbi1zZWNyZXQ=}")
    private String basicToken;  //token验证时选择 基础验证类型

    @Autowired
    private StringRedisTemplate strRedisTemplate;  //指定存储String类型

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private GeetestLib geetestLib; //极验对象

    /**
     * 普通用户登录：
     *      极验证的二次数据校验
     *      根据手机号/邮箱登录/用户名-password等 去授权服务器 验证 获取token
     *      将token缓存在redis中并设置过期时间
     *      封装需要的返回值数据，返回 封装对象
     *
     * @param loginForm 登录的表单参数
     * @return 登录的结果
     */
    @Override
    public LoginUser login(LoginForm loginForm) {
        log.info("用户{}开始登录", loginForm.getUsername());
        System.out.println("在前端密码已经被加密了: "+loginForm.getUsername()+"--"+loginForm.getPassword());

        //极验证的二次数据校验(用户验证后)
        checkFormData(loginForm);

        LoginUser loginUser = null;
        // 登录就是使用用户名和密码换一个token 而已--->远程调用->authorization-server的指定url -> 获取token
        ResponseEntity<JwtToken2> tokenResponseEntity = oAuth2FeignClient2.getToken2("password", loginForm.getUsername(), "123456", "member_type", basicToken);
        if (tokenResponseEntity.getStatusCode() == HttpStatus.OK) {
            JwtToken2 jwtToken2 = tokenResponseEntity.getBody();
            log.info("远程调用成功,结果为", JSON.toJSONString(jwtToken2, true));
            // token 必须包含 bearer(token类型)
            loginUser = new LoginUser(loginForm.getUsername(), jwtToken2.getExpiresIn(), jwtToken2.getTokenType() + " " + jwtToken2.getAccessToken(), jwtToken2.getRefreshToken());
            // 使用网关解决登出的问题:
            // token 是直接存储的
            strRedisTemplate.opsForValue().set(jwtToken2.getAccessToken(), "", jwtToken2.getExpiresIn(), TimeUnit.SECONDS);
        }
        return loginUser;
    }

    /**
     * 极验证的二次数据校验:
     *      1.封装数据
     *      2.去校验
     *      3.成功啥也不干，失败抛异常
     *
     * @param loginForm
     */
    private void checkFormData(LoginForm loginForm) {
        String challenge = loginForm.getGeetest_challenge();
        String validate = loginForm.getGeetest_validate();
        String seccode = loginForm.getGeetest_seccode();
        int status = 0;
        String userId = "";
        //取出之前缓存的 极验验证API服务状态Session Key、极验证里面的user
        String statusStr = redisTemplate.opsForValue().get(GeetestLib.GEETEST_SERVER_STATUS_SESSION_KEY).toString();
        status = Integer.valueOf(statusStr).intValue();
        userId = redisTemplate.opsForValue().get(GeetestLib.GEETEST_SERVER_USER_KEY + ":" + loginForm.getUuid()).toString();

        GeetestLibResult result = null;
        if (status == 1) {  //第一次交互: 成功失败的标识码，1表示成功，0表示失败
            /*
            自定义参数,可选择添加
                user_id 客户端用户的唯一标识，确定用户的唯一性；作用于提供进阶数据分析服务，可在register和validate接口传入，不传入也不影响验证服务的使用；若担心用户信息风险，可作预处理(如哈希处理)再提供到极验
                client_type 客户端类型，web：电脑上的浏览器；h5：手机上的浏览器，包括移动应用内完全内置的web_view；native：通过原生sdk植入app应用的方式；unknown：未知
                ip_address 客户端请求sdk服务器的ip地址
            */
            Map<String, String> paramMap = new HashMap<String, String>();
            paramMap.put("user_id", userId);
            paramMap.put("client_type", "web");
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            paramMap.put("ip_address", IpUtil.getIpAddr(servletRequestAttributes.getRequest()));
            //极验证的二次数据校验
            result = geetestLib.successValidate(challenge, validate, seccode, paramMap);
            log.info("验证的结果为{}", JSON.toJSONString(result));
        } else { //不成功
            result = geetestLib.failValidate(challenge, validate, seccode);
        }


        if(result.getStatus()!=1){ //极验证的二次数据校验的 校验状态码【成功啥也不干】
            log.error("验证异常",JSON.toJSONString(result,true));
            throw new IllegalArgumentException("验证码验证异常") ;
        }
    }
}
